/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.me.mjw.rememome.db;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.me.mjw.rememome.memo.Memo;
import org.me.mjw.rememome.memo.MemoCategory;
import org.me.mjw.rememome.memo.MemoStatus;

/**
 *
 * @author MWaldman
 */
public class ReMemoMeDbAdapter
{

    public enum OrderType
    {

        NEWEST,
        OLDEST,
        ALPHA,
        PRIORITY
    }
    public static final String KEY_SHORT_NAME = "shortName";
    public static final String KEY_LONG_NAME = "longName";
    public static final String KEY_MEMO = "memo";
    public static final String KEY_CAT = "category";
    public static final String KEY_STATUS = "status";
    public static final String KEY_CREATION_TIME = "creationTimestamp";
    public static final String KEY_STATUS_TIME = "statusTimestamp";
    public static final String KEY_PRIORITY = "priority";
    public static final String KEY_ROWID = "_id";
    private static final String TAG = "ReMemoMeDbAdapter";
    private DatabaseHelper mDbHelper;
    private SQLiteDatabase mDb;
    private static final String DATABASE_NAME = "rememome";
    private static final String DATABASE_TABLE_MEMOS = "memos";
    private static final String DATABASE_TABLE_CAT = "categories";
    private static final int DATABASE_VERSION = 2;
    private static final int DATABASE_OLD_VERSION = 1;
    private final Context mCtx;
    static private final SimpleDateFormat dateFormat = new SimpleDateFormat(
            "yyyy-MM-dd hh:mm:ss.SSS");
    static public int PRIORITY_MIN = 1;
    static public int PRIORITY_MAX = 1000;
    static public int PRIORITY_DEFAULT = 500;
    /**
     * Database creation sql statement
     */
    // Switch timestamp to type Integer, for easier sorting
    // And added Priority to track the importance of memo
    private static final String DATABASE_CREATE_MEMOS =
            "create table " + DATABASE_TABLE_MEMOS + " " +
            "(" + KEY_ROWID + " integer primary key autoincrement, " +
            KEY_MEMO + " text not null, " +
            KEY_CAT + " integer not null, " +
            KEY_STATUS + " integer not null, " +
            KEY_CREATION_TIME + " integer not null, " +
            KEY_STATUS_TIME + " integer not null, " +
            KEY_PRIORITY + " integer not null " +
            ");";
    private static final String DATABASE_CREATE_MEMOS_VER1 =
            "create table " + DATABASE_TABLE_MEMOS + " " +
            "(" + KEY_ROWID + " integer primary key autoincrement, " +
            KEY_MEMO + " text not null, " +
            KEY_CAT + " integer not null, " +
            KEY_STATUS + " integer not null, " +
            KEY_CREATION_TIME + " text not null, " +
            KEY_STATUS_TIME + " text not null" +
            ");";
    private static final String DATABASE_CREATE_CAT =
            "create table " + DATABASE_TABLE_CAT + " " +
            "(" + KEY_ROWID + " integer primary key autoincrement, " +
            KEY_SHORT_NAME + " text not null, " +
            KEY_LONG_NAME + " text not null" +
            ");";
    private static final String DATABASE_INIT_CAT1 =
            "insert into " + DATABASE_TABLE_CAT + " " +
            "(" +
            KEY_SHORT_NAME + ", " +
            KEY_LONG_NAME +
            ") " +
            "Values ('TO DO', 'Things to Do.');";
    private static final String DATABASE_INIT_CAT2 =
            "insert into " + DATABASE_TABLE_CAT + " " +
            "(" +
            KEY_SHORT_NAME + ", " +
            KEY_LONG_NAME +
            ") " +
            "Values ('TO GET', 'Things to Get.');";

    private static class DatabaseHelper extends SQLiteOpenHelper
    {

        DatabaseHelper(Context context)
        {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db)
        {
            // Create master table
            db.execSQL(DATABASE_CREATE_MEMOS);
            // Create category table then fill with with default categories
            db.execSQL(DATABASE_CREATE_CAT);
            db.execSQL(DATABASE_INIT_CAT1);
            db.execSQL(DATABASE_INIT_CAT2);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
        {
            Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion +
                    ", which will save data and convert it to new table formats.");
            // Copy old db to new db
            // Rename Tables
            renameTable(db, DATABASE_TABLE_MEMOS, DATABASE_TABLE_MEMOS + DATABASE_OLD_VERSION);
            renameTable(db, DATABASE_TABLE_CAT, DATABASE_TABLE_CAT + DATABASE_OLD_VERSION);
            // Create new tables
            // Create master table
            db.execSQL(DATABASE_CREATE_MEMOS);
            // Create category table
            db.execSQL(DATABASE_CREATE_CAT);

            // Transfer data to new tables
            transferMemos(db, DATABASE_TABLE_MEMOS + DATABASE_OLD_VERSION, DATABASE_TABLE_MEMOS);
            transferCategory(db, DATABASE_TABLE_CAT + DATABASE_OLD_VERSION, DATABASE_TABLE_CAT);

            // Delete Old version tables
            db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE_MEMOS + DATABASE_OLD_VERSION + " ");
            db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE_CAT + DATABASE_OLD_VERSION + " ");
//            onCreate(db);
        }

        private void transferMemos(SQLiteDatabase db, String fromTable, String toTable)
        {
            Cursor c;
            c = db.query(fromTable, new String[]
                    {
                        KEY_ROWID,
                        KEY_MEMO,
                        KEY_CAT,
                        KEY_STATUS,
                        KEY_CREATION_TIME,
                        KEY_STATUS_TIME
                    }, null, null, null, null, null);
            if (c.moveToFirst())
            {
                transferMemo(c, db, toTable);
            }
            while (c.moveToNext())
            {
                transferMemo(c, db, toTable);
            }
            c.close();
        }

        private void transferMemo(Cursor c, SQLiteDatabase db, String toTable)
        {
            Date date;
            int id;
            String memo;
            int catId;
            int statusId;
            String creationStr;
            String changeStr;
            Calendar creation;
            Calendar change;

            id = c.getInt(0);
            memo = c.getString(1);
            catId = c.getInt(2);
            statusId = c.getInt(3);
            creationStr = c.getString(4);
            changeStr = c.getString(5);
            try
            {
                date = dateFormat.parse(creationStr);
                creation = new GregorianCalendar();
                creation.setTime(date);
            } catch (ParseException ex)
            {
                creation = Calendar.getInstance();
            }
            try
            {
                date = dateFormat.parse(changeStr);
                change = new GregorianCalendar();
                change.setTime(date);
            } catch (ParseException ex)
            {
                change = Calendar.getInstance();
            }
            insertMemoData(db, toTable, id, memo, catId, statusId, creation, change);
        }

        private void insertMemoData(SQLiteDatabase db, String toTable, int id, String memo,
                int catId, int statusId, Calendar creation, Calendar change)
        {
            ContentValues initialValues = new ContentValues();
            initialValues.put(KEY_ROWID, id);
            initialValues.put(KEY_MEMO, memo);
            initialValues.put(KEY_CAT, catId);
            initialValues.put(KEY_STATUS, statusId);
            initialValues.put(KEY_CREATION_TIME, creation.getTimeInMillis());
            initialValues.put(KEY_STATUS_TIME, change.getTimeInMillis());
            // Insert the default priority in new field
            initialValues.put(KEY_PRIORITY, PRIORITY_DEFAULT);

            db.insert(toTable, null, initialValues);
        }

        private void transferCategory(SQLiteDatabase db, String fromTable, String toTable)
        {
            Cursor c;
            c = db.query(fromTable, new String[]
                    {
                        KEY_ROWID,
                        KEY_SHORT_NAME,
                        KEY_LONG_NAME
                    }, null, null, null, null, null);
            if (c.moveToFirst())
            {
                transferCategory(c, db, toTable);
            }
            while (c.moveToNext())
            {
                transferCategory(c, db, toTable);
            }
            c.close();
        }

        private void transferCategory(Cursor c, SQLiteDatabase db, String toTable)
        {
            int id;
            String shortName;
            String longName;

            id = c.getInt(0);
            shortName = c.getString(1);
            longName = c.getString(2);
            insertCategoryData(db, toTable, id, shortName, longName);
        }

        private void insertCategoryData(SQLiteDatabase db, String toTable, int id, String shortName,
                String longName)
        {
            ContentValues initialValues = new ContentValues();
            initialValues.put(KEY_ROWID, id);
            initialValues.put(KEY_SHORT_NAME, shortName);
            initialValues.put(KEY_LONG_NAME, longName);

            db.insert(toTable, null, initialValues);
        }

        private void renameTable(SQLiteDatabase db, String fromTable, String toTable)
        {
            String query = "alter table " + fromTable + " " +
                    "rename to " + toTable + "; ";
            db.execSQL(query);

        }
    }

    /**
     * Constructor - takes the context to allow the database to be
     * opened/created
     *
     * @param ctx the Context within which to work
     */
    public ReMemoMeDbAdapter(Context ctx)
    {
        this.mCtx = ctx;
    }

    /**
     * Open the notes database. If it cannot be opened, try to create a new
     * instance of the database. If it cannot be created, throw an exception to
     * signal the failure
     *
     * @return this (self reference, allowing this to be chained in an
     *         initialization call)
     * @throws SQLException if the database could be neither opened or created
     */
    public ReMemoMeDbAdapter open() throws SQLException
    {
        mDbHelper = new DatabaseHelper(mCtx);
        mDb = mDbHelper.getWritableDatabase();
        return this;
    }

    public void close()
    {
        mDbHelper.close();
    }

    /*
     * Memo Functions
     */
    /**
     * Return a memo
     */
    private Memo convertToMemo(Cursor c)
    {
        long dateLong;
        Date date;
        Calendar cal;
        Memo memo;
        int statusId;
        MemoStatus status;
        int catId;
        MemoCategory cat;
        int priority;
        if (c != null)
        {
            memo = new Memo();
            memo.setId(c.getInt(0));
            memo.setMemo(c.getString(1));
            catId = c.getInt(2);
            cat = getCategory(catId);
            memo.setCategory(cat);
            statusId = c.getInt(3);
            status = MemoStatus.getStatus(statusId);
            memo.setStatus(status);
            dateLong = c.getLong(4);
            cal = new GregorianCalendar();
            cal.setTimeInMillis(dateLong);
            memo.setCreationTimestamp(cal);
            dateLong = c.getLong(5);
            cal = new GregorianCalendar();
            cal.setTimeInMillis(dateLong);
            memo.setStatusTimestamp(cal);
            memo.setPriority(c.getInt(6));
            return memo;
        }
        else
        {
            return null;
        }
    }

    /**
     * Create a new note using the title and body provided. If the note is
     * successfully created return the new rowId for that note, otherwise return
     * a -1 to indicate failure.
     *
     * @param title the title of the note
     * @param body the body of the note
     * @return rowId or -1 if failed
     */
    public long insertMemo(Memo memo)
    {
        return insertMemo(memo.getMemo(), memo.getCategory().getId(), memo.getStatus().getStatusId(),
                memo.getCreationTimestamp().getTime(), memo.getStatusTimestamp().getTime(),
                memo.getPriority());
    }

    public long insertMemo(String memo, int category, int status, Date creationTimestamp,
            Date statusTimestamp, int priority)
    {
        ContentValues initialValues = new ContentValues();
        initialValues.put(KEY_MEMO, memo);
        initialValues.put(KEY_CAT, category);
        initialValues.put(KEY_STATUS, status);
        initialValues.put(KEY_CREATION_TIME, creationTimestamp.getTime());
        initialValues.put(KEY_STATUS_TIME, statusTimestamp.getTime());
        initialValues.put(KEY_PRIORITY, priority);

        return mDb.insert(DATABASE_TABLE_MEMOS, null, initialValues);
    }

    /**
     * Update the note using the details provided. The note to be updated is
     * specified using the rowId, and it is altered to use the title and body
     * values passed in
     *
     * @param rowId id of note to update
     * @param title value to set note title to
     * @param body value to set note body to
     * @return true if the note was successfully updated, false otherwise
     */
    public boolean updateMemo(Memo memo)
    {
        return updateMemo(memo.getId(), memo.getMemo(), memo.getCategory().getId(),
                memo.getStatus().getStatusId(),
                memo.getCreationTimestamp().getTime(), memo.getStatusTimestamp().getTime(),
                memo.getPriority());
    }

    public boolean updateMemo(long rowId, String memo, int category, int status,
            Date creationTimestamp, Date statusTimestamp, int priority)
    {
        ContentValues args = new ContentValues();
        args.put(KEY_MEMO, memo);
        args.put(KEY_CAT, category);
        args.put(KEY_STATUS, status);
        args.put(KEY_CREATION_TIME, dateFormat.format(creationTimestamp));
        args.put(KEY_STATUS_TIME, dateFormat.format(statusTimestamp));
        args.put(KEY_PRIORITY, priority);

        return mDb.update(DATABASE_TABLE_MEMOS, args, KEY_ROWID + "=" + rowId, null) > 0;
    }

    /**
     * Delete the note with the given rowId
     *
     * @param rowId id of note to delete
     * @return true if deleted, false otherwise
     */
    public boolean deleteMemo(long rowId)
    {

        return mDb.delete(DATABASE_TABLE_MEMOS, KEY_ROWID + "=" + rowId, null) > 0;
    }

    public boolean deleteMemo(String memo)
    {

        return mDb.delete(DATABASE_TABLE_MEMOS, KEY_MEMO + "='" + memo + "'", null) > 0;
    }

    public Cursor fetchAllMemos()
    {
        return fetchAllMemos(null);
    }

    public Cursor fetchAllMemos(OrderType orderType)
    {
        String order;

        if (orderType == null)
        {
            order = null;
        }
        else
        {
            switch (orderType)
            {
                case ALPHA:
                    order = KEY_MEMO + " COLLATE NOCASE";
                    break;
                case NEWEST:
                    order = KEY_CREATION_TIME + " DESC";
                    break;
                case OLDEST:
                    order = KEY_CREATION_TIME;
                    break;
                case PRIORITY:
                    order = KEY_PRIORITY;
                    break;
                default:
                    order = null;
                    break;
            }
        }

        return mDb.query(DATABASE_TABLE_MEMOS, new String[]
                {
                    KEY_ROWID,
                    KEY_MEMO,
                    KEY_CAT,
                    KEY_STATUS,
                    KEY_CREATION_TIME,
                    KEY_STATUS_TIME,
                    KEY_PRIORITY
                }, null, null, null, null, order);
    }

    public List<Memo> listMemos()
    {
        return listMemos(null);
    }

    public List<Memo> listMemos(OrderType orderType)
    {
        Cursor c;
        List<Memo> list;
        Memo memo;

        list = new ArrayList<Memo>();

        c = fetchAllMemos(orderType);

        if (c.moveToFirst())
        {
            memo = convertToMemo(c);
            list.add(memo);
        }
        while (c.moveToNext())
        {
            memo = convertToMemo(c);
            list.add(memo);
        }
        return list;
    }

    /**
     * Return a Cursor positioned at the note that matches the given rowId
     *
     * @param rowId id of note to retrieve
     * @return Cursor positioned to matching note, if found
     * @throws SQLException if note could not be found/retrieved
     */
    public Cursor fetchMemo(long rowId)
    {

        Cursor mCursor =
                mDb.query(true, DATABASE_TABLE_CAT, new String[]
                {
                    KEY_ROWID,
                    KEY_MEMO,
                    KEY_CAT,
                    KEY_STATUS,
                    KEY_CREATION_TIME,
                    KEY_STATUS_TIME,
                    KEY_PRIORITY
                }, KEY_ROWID + "=" + rowId, null,
                null, null, null, null);
        if (mCursor != null)
        {
            mCursor.moveToFirst();
        }
        return mCursor;

    }

    public Memo getMemo(long rowId)
    {
        Cursor c;
        Memo m;

        c = fetchMemo(rowId);
        m = convertToMemo(c);

        return m;
    }

    /*
     * Memo Category Functions
     */
    /**
     * Return a Memo Category
     */
    private MemoCategory convertToMemoCategory(Cursor c)
    {
        MemoCategory mc;
        if (c != null)
        {
            mc = new MemoCategory();
            mc.setId(c.getInt(0));
            mc.setShortName(c.getString(1));
            mc.setLongName(c.getString(2));
            return mc;
        }
        else
        {
            return null;
        }
    }

    public long insertCategory(MemoCategory mc)
    {
        return insertCategory(mc.getShortName(), mc.getShortName());
    }

    public long insertCategory(String shortName, String longName)
    {
        ContentValues initialValues = new ContentValues();
        initialValues.put(KEY_SHORT_NAME, shortName);
        initialValues.put(KEY_LONG_NAME, longName);

        return mDb.insert(DATABASE_TABLE_CAT, null, initialValues);
    }

    public boolean deleteCategory(MemoCategory mc)
    {
        return deleteCategory(mc.getId());
    }

    public boolean deleteCategory(long rowId)
    {
        return mDb.delete(DATABASE_TABLE_CAT, KEY_ROWID + "=" + rowId, null) > 0;
    }

    /**
     * Return a Cursor positioned at the note that matches the given rowId
     *
     * @param rowId id of note to retrieve
     * @return Cursor positioned to matching note, if found
     * @throws SQLException if note could not be found/retrieved
     */
    public Cursor fetchCategory(long rowId) throws SQLException
    {

        Cursor mCursor =
                mDb.query(true, DATABASE_TABLE_CAT, new String[]
                {
                    KEY_ROWID,
                    KEY_SHORT_NAME,
                    KEY_LONG_NAME
                }, KEY_ROWID + "=" + rowId, null,
                null, null, null, null);
        if (mCursor != null)
        {
            mCursor.moveToFirst();
        }
        return mCursor;
    }

    public MemoCategory getCategory(long rowId)
    {
        Cursor c;
        MemoCategory mc;

        c = fetchCategory(rowId);
        mc = convertToMemoCategory(c);

        return mc;
    }

    public Cursor fetchAllCategories()
    {

        return mDb.query(DATABASE_TABLE_CAT, new String[]
                {
                    KEY_ROWID,
                    KEY_SHORT_NAME,
                    KEY_LONG_NAME
                }, null, null, null, null, null);
    }

    public List<MemoCategory> listCategories()
    {
        Cursor c;
        List<MemoCategory> list;
        MemoCategory mc;

        list = new ArrayList<MemoCategory>();

        c = fetchAllCategories();

        if (c.moveToFirst())
        {
            mc = convertToMemoCategory(c);
            list.add(mc);
        }
        while (c.moveToNext())
        {
            mc = convertToMemoCategory(c);
            list.add(mc);
        }
        return list;
    }
}
